package br.com.sgpv2.hibernate.pessoa;

import java.util.Collections;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import br.com.sgpv2.entity.cargos.CargoFuncao;
import br.com.sgpv2.entity.organograma.Departamento;
import br.com.sgpv2.entity.organograma.Nivel;
import br.com.sgpv2.entity.organograma.Secretaria;
import br.com.sgpv2.entity.pessoa.Pessoa;
import br.com.sgpv2.entity.pessoa.PessoaDTO;
import br.com.sgpv2.entity.pessoa.endereco.Cidade;
import br.com.sgpv2.entity.pessoa.endereco.Estado;
import br.com.sgpv2.entity.pessoa.endereco.Pais;
import br.com.sgpv2.hibernate.infra.SGPGenericDAO;

@Repository(value = "pessoaRepository")
public class PessoaHibernateDAO extends SGPGenericDAO<Pessoa> implements
		PessoaRepository {

	private static final String NULL = "null";

	/**
	 * Construtor padrão.
	 * 
	 */
	@Autowired
	public PessoaHibernateDAO(
			@Qualifier("sessionFactory") SessionFactory factory) {
		super(Pessoa.class);
		super.setSessionFactory(factory);
	}

	/**
	 * Consulta todas as cidades de um determinado estado.
	 * 
	 * @param siglaEstado
	 *            o estado a ser usado no filtro.
	 */
	public List<Cidade> consultarCidadesPorEstado(String siglaEstado) {
		Criteria criteria = createCriteria(Cidade.class);
		criteria.createAlias("estado", "e");
		criteria.add(Restrictions.eq("e.siglaEstado", siglaEstado));

		return Collections.checkedList((List<Cidade>) criteria.list(),
				Cidade.class);
	}

	/**
	 * Consulta todos os estados por pais.
	 * 
	 * @param pais
	 *            o pa�s a ser usado no filtro.
	 */
	public List<Estado> consultarEstadosPorPais(Pais pais) {
		Criteria criteria = createCriteria(Estado.class);
		criteria.add(Restrictions.eq("pais", pais));
		return Collections.checkedList((List<Estado>) criteria.list(),
				Estado.class);
	}

	/**
	 * Consulta todos os pa�ses.
	 * 
	 */
	public List<Pais> consultarPaises() {
		Criteria criteria = createCriteria(Pais.class);
		criteria.add(Restrictions.isNotNull("siglaPais"));
		return Collections
				.checkedList((List<Pais>) criteria.list(), Pais.class);
	}

	/**
	 * Lista as pessoas do banco de dados transformando o resultado no DTO para
	 * mostrar na tela.
	 * 
	 */
	public List<PessoaDTO> consultarPessoaDTO() {
		Criteria criteria = createCriteria(Pessoa.class);
		criteria.createAlias("documentacao", "doc");
		criteria.createAlias("doc.identidade", "identidade");
		criteria.add(Restrictions.isNotNull("nome"));

		transformarDTOPessoa(criteria);

		return Collections.checkedList((List<PessoaDTO>) criteria.list(),
				PessoaDTO.class);
	}

	private void transformarDTOPessoa(Criteria criteria) {
		criteria.setProjection(
				Projections
						.projectionList()
						.add(Projections.id().as("pkPessoa"))
						.add(Projections.property("nome").as("nomePessoa"))
						.add(Projections.property("dataNascimento").as(
								"dataNascimento"))
						.add(Projections.property("identidade.numero").as(
								"identidade"))
						.add(Projections.property("doc.cpf").as("cpf"))
						)
				.setResultTransformer(
						new AliasToBeanResultTransformer(PessoaDTO.class))
				.addOrder(Order.asc("nome"));
	}

	/**
	 * Lista as pessoas do banco de dados transformando o resultado no DTO para
	 * mostrar na tela mediante aos filtros informados.
	 * 
	 */
	public List<PessoaDTO> consultarPessoasDTOPorFiltro(String nome,
			String cpf, String identidade) {

		Criteria criteria = createCriteria(Pessoa.class);
		criteria.createAlias("documentacao", "doc");
		criteria.createAlias("doc.identidade", "identidade");

		if (nome != null && !nome.isEmpty() && !nome.equals(NULL)) {
			criteria.add(Restrictions.like("nome", nome, MatchMode.ANYWHERE));
		}
		if (cpf != null && !cpf.isEmpty()) {
			criteria.add(Restrictions.like("doc.cpf", cpf, MatchMode.ANYWHERE));
		}
		if (identidade != null && !identidade.isEmpty()) {
			criteria.add(Restrictions.like("identidade.numero", identidade,
					MatchMode.ANYWHERE));
		}

		transformarDTOPessoa(criteria);

		return Collections.checkedList((List<PessoaDTO>) criteria.list(),
				PessoaDTO.class);
	}

	@Override
	public Pais buscarPaisPorNome(String paisTransiente) {
		Criteria criteria = createCriteria(Pais.class);
		criteria.add(Restrictions.eq("nomePais", paisTransiente));

		return (Pais) criteria.uniqueResult();
	}

	@Override
	public Estado buscarEstadoPorNome(String estadoTransiente) {
		Criteria criteria = createCriteria(Estado.class);
		criteria.add(Restrictions.eq("siglaEstado", estadoTransiente));

		return (Estado) criteria.uniqueResult();
	}

	@Override
	public Cidade buscarCidadePorNome(String cidadeTransiente) {
		Criteria criteria = createCriteria(Cidade.class);
		criteria.add(Restrictions.eq("nomeCidade", cidadeTransiente));

		return (Cidade) criteria.uniqueResult();
	}

	@Override
	public List<Nivel> getNiveis() {
		Criteria criteria = createCriteria(Nivel.class);
		criteria.add(Restrictions.isNotNull("codigo"));
		return Collections.checkedList((List<Nivel>) criteria.list(),
				Nivel.class);
	}

	@Override
	public List<CargoFuncao> getCargosFuncoes() {
		Criteria criteria = createCriteria(CargoFuncao.class);
		criteria.add(Restrictions.isNotNull("codigo"));
		return Collections.checkedList((List<CargoFuncao>) criteria.list(),
				CargoFuncao.class);
	}

	@Override
	public List<Departamento> getDepartamentos() {
		Criteria criteria = createCriteria(Departamento.class);
		criteria.add(Restrictions.isNotNull("codigo"));
		return Collections.checkedList((List<Departamento>) criteria.list(),
				Departamento.class);
	}

	@Override
	public List<Secretaria> getSecretarias() {
		Criteria criteria = createCriteria(Secretaria.class);
		criteria.add(Restrictions.isNotNull("codigo"));
		return Collections.checkedList((List<Secretaria>) criteria.list(),
				Secretaria.class);
	}

	@Override
	public Secretaria buscarSecretariaPorDescricao(String descricao) {
		Criteria criteria = createCriteria(Secretaria.class);
		criteria.add(Restrictions.eq("descricao", descricao));

		return (Secretaria) criteria.uniqueResult();
	}

	@Override
	public Departamento buscarDepartamentoPorDescricao(String descricao) {
		Criteria criteria = createCriteria(Departamento.class);
		criteria.add(Restrictions.eq("descricao", descricao));

		return (Departamento) criteria.uniqueResult();
	}

	@Override
	public CargoFuncao buscarCargoFuncaoPorDescricao(String descricao) {
		Criteria criteria = createCriteria(CargoFuncao.class);
		criteria.add(Restrictions.eq("descricao", descricao));

		return (CargoFuncao) criteria.uniqueResult();
	}

	@Override
	public Nivel buscarNivelPorDescricao(String codigo) {
		Criteria criteria = createCriteria(Nivel.class);
		criteria.add(Restrictions.eq("codigo", codigo));

		return (Nivel) criteria.uniqueResult();
	}
}
